首先我們先建立一個名為 SearchBar 的 component,並且在其中加上一個用於顯示資料的視窗 & 視窗的開關。
import React, { useState } from "react";
import cx from "classnames";
const Modal = ({ isOpen, onClose, originArr }) => {
const [inputValue, setInputValue] = useState("");
const [filteredData, setFilteredData] = useState([]);
const changeCase = (inputString, toLowerCase) => {
if (toLowerCase) {
return inputString.toLowerCase();
} else {
return inputString.toUpperCase();
}
};
const filterFunc = (keyword) => {
const newKeyword = changeCase(keyword, true);
const filteredData = originArr.data?.filter((data) => {
return data.title.toLowerCase().includes(newKeyword)&& keyword !== "";
});
return filteredData;
};
const handleOnChange = (e) => {
setInputValue(e.target.value);
const result = filterFunc(e.target.value);
setFilteredData(result);
};
return (
<div
className={cx(
"gap-[20px] rounded-xl absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 bg-white p-5 z-50 shadow-md border-2 flex flex-wrap justify-center items-center py-10 px-20",
{ block: isOpen, hidden: !isOpen }
)}
>
<div>
<label className="text-2xl">
請輸入資料:
<input
type="text"
value={inputValue}
onChange={(e) => handleOnChange(e)}
className="border-2"
/>
</label>
</div>
<div className="w-full text-center"></div>
<ul className="self-start w-full">
{filteredData?.length !== 0 && (
<div className="w-full text-center">
{filteredData &&
filteredData.map(
(data, index) =>
data && (
<li
className="my-2 py-2 border-b-2 flex justify-between"
data-todo={`todo${index}`}
key={index}
>
<span className="flex-1">{data.title}</span>
<div className="flex gap-[20px]"></div>
</li>
)
)}
</div>
)}
</ul>
<button
onClick={onClose}
className="bg-red-600 text-white px-4 py-2 rounded-md absolute right-2 top-2"
>
X
</button>
</div>
);
};
/**
* @description a component include toggleBtn & Modal
* @param {array} data
*/
const SearchBar = (data) => {
const [isModalOpen, setIsModalOpen] = useState(false);
const handleOpenModal = () => {
setIsModalOpen(true);
};
const handleCloseModal = () => {
setIsModalOpen(false);
};
return (
<div>
<button
onClick={handleOpenModal}
className="self-center min-w-[80px] text-center rounded mx-2 border-2 px-4 py-1 cursor-pointer shadow-md transition-all bg-white hover:bg-[#e3e8eB] text-gray-500 text-xl font-bold"
>
搜尋todo
</button>
<Modal isOpen={isModalOpen} onClose={handleCloseModal} originArr={data} />
</div>
);
};
export default SearchBar;
我們可以看到這個 component 擁有一個顯示資料的視窗 & 視窗的開關,並且 Modal 接收的參數分別為是否顯示&原始資料,而SearchBar則接收來自於父層的完整資料。
接下來我們整理一下資料流,看看是否和我們預期的一致。
首先SearchBar負責接收父層傳入的 array,接下來資料會進入到Modal,當 user 在輸入欄輸入資料的時候就會觸發onChange,這時候我們就可以得到 keyWord 的資料,並且 keyword 會透過changeCase這個 Function 轉變為小寫,而剛剛傳入的資料則會透過 filter 這個 Method 找出 title 包含 keyWord 的資料,並回傳給我們,最後我們將篩選後的資料塞進filteredData,最後在 return 的部分,使用 map 將我們獲得的資料 render 到畫面上。
這邊需要注意的地方就在於這個部分
const filteredData = originArr.data?.filter((data) => {
return data.title.toLowerCase().includes(newKeyword) && keyword !== "";
});
至於為什麼呢,因為當user將輸入欄位的資料清空,這時候條件會是string.includes(''),並且這會是成立的,所以我們會發現所有的資料都被return進入到filteredData中,所以這邊我們需要加上一個條件,keyword不能是空的。
這樣才能保證我們的的資料不會全部被render出來。